package fserv.fileshare;

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import fserv.control.ClientController;

/**
 * File share module: File Listener.
 * 
 * Launches a thread and opens a listening socket to share a local file.
 * 
 * @author Arthur Bit-Monnot <abit@etud.insa-toulouse.fr>
 * @author Julien Marchand <jmarchan@etud.insa-toulouse.fr>
 */
public class FileListener extends Thread {
    
    /**
     * Time to live of a FileListener.
     * After this time, the FileListener will be destroyed.
     * In milliseconds.
     */
    private static final int TTL = 30 * 1000;
    
    /**
     * Minimum port to be used.
     */
    private static final int MIN_PORT = 5678;
    
    /**
     * Maximum port to be used.
     */
    private static final int MAX_PORT = 6789;
    
    /**
     * Reference to the Chat Controller.
     */
    private ClientController ctrl;
    
    /**
     * Local file to share.
     */
    private File file;
    
    /**
     * Port chosen to bind the listening socket.
     */
    private int port;
    
    /**
     * Listening socket used to accept incoming connections.
     */
    private ServerSocket listenSocket;
    
    /**
     * Constructor of a FileListener.
     * Chooses a port between MIN_PORT and MAX_PORT to bind the listening
     * socket.
     * 
     * @param ctrl the reference to the Chat Controller
     * @param file the local file to share
     * @throws IOException if the local file does not exist
     */
    public FileListener(ClientController ctrl, File file) throws IOException {
        this.ctrl = ctrl;
        this.file = file;
        this.port = MIN_PORT;
        
        boolean bindOk = false;
        while (!bindOk && port <= MAX_PORT) {
            try {
                listenSocket = new ServerSocket(port);
                bindOk = true;
            } catch (IOException ex) {
                if (port == MAX_PORT) {
                    throw ex;
                } else {
                    port++;
                }
            }
        }
    }

    /**
     * Gets the port chosen to bind the listening socket.
     * 
     * @return the port chosen to bind the listening socket.
     */
    public int getPort() {
        return port;
    }
    
    /**
     * Opens a listening socket to share a local file and waits for incoming
     * connections.
     * Creates FileSender objects to handle incoming connections.
     */
    @Override
    public void run() {
        Logger.getLogger(FileListener.class.getName()).log(Level.INFO, "Starting.");
        
        ctrl.fileShareStarted(file);
        
        // Self-destruct in 30 seconds
        new Timer(true).schedule(new TimerTask() {
            public void run() {
                FileListener.this.interrupt();
                try {
                    FileListener.this.listenSocket.close();
                } catch (IOException ex) {
                    Logger.getLogger(FileListener.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        },  TTL);
        
        try {
            while (true) {
                Socket clientSocket = listenSocket.accept();
                if (listenSocket != null) {
                    FileSender sender = new FileSender(ctrl, file, clientSocket);
                    sender.start();
                }
            }
        } catch (IOException ex) {
            if (Thread.currentThread().isInterrupted()) {
                Thread.interrupted();
                Logger.getLogger(FileListener.class.getName()).log(Level.INFO, "Graceful stop.");
            } else {
                Logger.getLogger(FileListener.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
       
        ctrl.fileShareStopped(file);
        
        Logger.getLogger(FileListener.class.getName()).log(Level.INFO, "Exiting.");
    }
}
